package com.qf.ability.gateway.filter;

import com.qf.ability.gateway.application.AuthIgnoreConfig;
import com.qf.ability.gateway.utils.ResponseUtils;
import com.qf.commons.base.result.Codes;
import com.qf.commons.core.utils.JwtUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;
import java.util.List;

/**
 * 处理认证信息的全局过滤器
 * author Ken
 * create_time 2022/11/28
 */
@Component
@Slf4j
//统一配置中心的配置 实时刷新时，必须添加该注解
@RefreshScope
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    /**
     * 注入资源配置类
     */
    @Autowired(required = false)
    private AuthIgnoreConfig authIgnoreConfig;

    /**
     * Spring提供的Url匹配工具 可以匹配通配符的 /auth/**  /auth/a
     */
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取当前请求的url
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        log.debug("[auth global filter] 全局认证拦截器 - {}", path);

        //判断当前的请求是否必须认证通过后才能放行
        //获取所有要放行的资源【无需登录的资源列表】
        List<String> paths = authIgnoreConfig.getPaths();
        if (paths != null) {
            //循环判断当前请求的url是否在无需登录的资源列表中
            for (String ignorePath : paths) {
                if (antPathMatcher.match(ignorePath, path)){
                    log.debug("[auth global filter] 当前请求无需登录即可访问 - {}", path);
                    return chain.filter(exchange);
                }
            }
        }

        log.debug("[auth global filter] 当前请求必须登录才可访问 - {}", path);
        //获取请求头中的登录令牌
        HttpHeaders headers = request.getHeaders();
        String jwtToken = headers.getFirst("loginToken");

        //如果令牌为空，说明当前客户端没有登录
        if(jwtToken == null){
            //告知客户端必须登录才行
            return ResponseUtils.result(exchange, Codes.AUTH_ERROR, null, HttpStatus.INTERNAL_SERVER_ERROR);
        }

        //校验jwt令牌（1、判断是否被篡改过 2、判断是否已经过期）
        String userJson = (String) JwtUtils.parseJwtToken(jwtToken, "user");
        if (userJson == null) {
            //告知客户端必须登录才行
            return ResponseUtils.result(exchange, Codes.AUTH_ERROR, null, HttpStatus.INTERNAL_SERVER_ERROR);
        }

        //令牌有效
        log.debug("[auth global filter] 令牌校验通过 - {} - json：{}", path, userJson);

        //将用户的json 加入到请求头中，继续往后传递
        //--------------------------------------------------------------
        HttpHeaders newHttpHeaders = new HttpHeaders();
        //先将旧的请求头的所有数据 复制到新的请求头中
        newHttpHeaders.putAll(headers);
        //将解析出来的用户json，放入到请求头中
        //通过URLEncoder编码的方式 解决请求头中中文乱码的问题
        String content = URLEncoder.encode(userJson, "UTF-8");
        newHttpHeaders.set("userJson", content);
        //再将登录令牌的请求头移除掉
        newHttpHeaders.remove("loginToken");

        //构建一个新的请求
        ServerHttpRequest newRequest = request.mutate().uri(request.getURI()).build();
        //将新的请求构建成一个装饰者对象
        newRequest = new ServerHttpRequestDecorator(newRequest) {
            @Override
            public HttpHeaders getHeaders() {
                return newHttpHeaders;
            }
        };

        //直接放行,基于请的请求头
        return chain.filter(exchange.mutate().request(newRequest).build());
    }

    @Override
    public int getOrder() {
        return -100;
    }
}
